package com.nxy.cpfsfe.busi.btmgtsk.endd;

import com.adtec.starring.exception.BaseException;
import com.nxy.cpfsfe.enumtype.RespCd;
import com.nxy.cpfsfe.mybatis.mapper.*;
import com.nxy.cpfsfe.mybatis.pojo.*;
import com.nxy.cpfsfe.util.DateUtil;
import com.nxy.cpfsfe.util.zookeeper.DistributedLock;
import com.nxy.cpfsfe.util.zookeeper.ZookeeperLockUtil;
import org.apache.ibatis.session.SqlSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Date;
import java.util.List;

public abstract class AbsEndDChkASrv {
	
	private static final Logger LOGGER = LoggerFactory.getLogger(AbsEndDChkASrv.class);

	private final static Logger monitorLOGGER = LoggerFactory.getLogger("monitor");
	
	protected String bizSysId;
	
	protected String pCId;
	
	protected Date chkAD;
	
	protected DistributedLock distributedLock = null;
	
	protected SqlSession sqlSession;
	
	protected TeBizSett teBizSett;
	
	protected String step;

	protected String starringModelNm;
	
	/**
	 * 
	 * Description:日终生成对账文件主流程
	 *
	 * @author jjw
	 * @data 2019年9月12日
	 */
	public void endDChkAProcess(){
		
		try{
			LOGGER.info("开始生成业务系统[{}]的[{}]通道[{}]日对账文件",bizSysId,pCId,DateUtil.date3Str(chkAD));
			chkAProcessing();
			chkStep();
			chkData();
			canChkA();
			chkNumAndAmt();
			chkAByStep();
			LOGGER.info("业务系统[{}]的[{}]通道[{}]日对账文件生成完毕",bizSysId,pCId,DateUtil.date3Str(chkAD));
			monitorLOGGER.info("业务系统[{}]的[{}]通道[{}]日对账文件生成完毕",bizSysId,pCId,DateUtil.date3Str(chkAD));
		}catch(BaseException e){
			if(e.getErrorCode().equals(RespCd.NCPI0000)){
				LOGGER.info(e.getErrorDesc());
			}else if(e.getErrorCode().equals(RespCd.NCPO0001) || e.getErrorCode().equals(RespCd.NCPS7001)){
				throw e;
			}else{
				updateChkASchedule(null,"2",null,null,null,e.getErrorDesc());
				throw e;
			}
		}catch(Throwable e){
			String errMsg = String.format("业务系统[%s]的[%s]通道[%s]日对账文件生成失败,出现未知异常", bizSysId,pCId,DateUtil.date3Str(chkAD));
			updateChkASchedule(null,"2",null,null,null,errMsg + e.getMessage());
			throw new BaseException(RespCd.NCPS9004,e,errMsg);
		}finally{
			if(distributedLock != null){
				distributedLock.unlock();
			}
		}
		
	}

	/**
	 *
	 * Description:检查是否有线程正在生成该业务系统的对账文件
	 *
	 * @author jjw
	 * @data 2019年9月12日
	 */
	private void chkAProcessing(){

		distributedLock = ZookeeperLockUtil.getInstance().creaZooKeeperLock(bizSysId + "_" + pCId + "_" + DateUtil.date4Str(chkAD));

		if(!distributedLock.tryLock()){
			String errMsg = String.format("业务系统[%s]正在生成[%s]通道[%s]日的对账文件，请勿重复操作", bizSysId,pCId,DateUtil.date3Str(chkAD));
			throw new BaseException(RespCd.NCPO0001,errMsg);
		}

	}

	/**
	 *
	 * Description:检查该业务系统该通道是否已进行过对账流程
	 *
	 * @author jjw
	 * @data 2019年9月12日
	 */
	private void chkStep(){

		TeBizSettMapper teBizSettMapper = sqlSession.getMapper(TeBizSettMapper.class);
		TeBizSett teBizSettQR = teBizSettMapper.queryBizChlChkA(teBizSett);

		if(teBizSettQR == null){
			teBizSett.setStep("0");
			teBizSett.setChkASta("0");
			teBizSett.setChkABgnTm(new Date());
			teBizSett.setCreTm(new Date());
			teBizSettMapper.insertBizChlChkA(teBizSett);
			step = teBizSett.getStep();
		}else{
			if("1".equals(teBizSettQR.getChkASta())){
				try{
					ntcBizSys();
				}catch (BaseException e){
					LOGGER.error("下发对账文件通知出错",e);
					throw new BaseException(RespCd.NCPS7001,"下发对账文件通知失败");
				}
				throw new BaseException(RespCd.NCPI0000,"下发对账文件通知成功");
			}else{
				updateChkASchedule(null,"0",null,null,null,"无");
				step = teBizSettQR.getStep();
			}
		}

	}
	
	/**
	 * 
	 * Description:检查数据的正确性
	 *
	 * @author jjw
	 * @data 2019年9月12日
	 */
	private void chkData(){
		
		if(chkAD == null){
			String errMsg = String.format("对账日期为空，无法生成业务系统[%s]的[%s]通道对账文件", bizSysId,pCId);
			throw new BaseException(RespCd.NCPO1019,errMsg);
		}
		
		//查询业务系统是否存在或启用
		ToBizSysMapper toBizSysMapper = sqlSession.getMapper(ToBizSysMapper.class);
		ToBizSys toBizSyse = toBizSysMapper.queryBizSysIsEnb(bizSysId);
		
		if(toBizSyse == null){
			String errMsg = String.format("业务系统[%s]不存在或未启用，无法生成[%s]通道[%s]日对账文件", bizSysId,pCId,DateUtil.date3Str(chkAD));
			throw new BaseException(RespCd.NCPS9004,errMsg);
		}
		
		//查询业务系统是否支持对账通道
		TrSysBizTypMapper trSysBizTypMapper = sqlSession.getMapper(TrSysBizTypMapper.class);
		List<TrSysBizTyp> trSysBizTyps = trSysBizTypMapper.queryOrgIdByBizSysId(bizSysId);
		
		TrChlBizTypMapper trChlBizTypMapper = sqlSession.getMapper(TrChlBizTypMapper.class);
		List<String> pCIds = trChlBizTypMapper.queryOpenChlByOrgId(trSysBizTyps);
		
		if(!pCIds.contains(pCId)){
			String errMsg = String.format("业务系统[%s]未开通[%s]通道，无法生成该通道[%s]日对账文件", bizSysId,pCId,DateUtil.date3Str(chkAD));
			throw new BaseException(RespCd.NCPS9004,errMsg);
		}
		
	}
	
	/**
	 * 
	 * Description:检查是否可以生成对账文件
	 *
	 * @author jjw
	 * @data 2019年9月12日
	 */
	private void canChkA(){
		
		//查询每个机构开通了哪些通道
		TrChlBizTypMapper trChlBizTypMapper = sqlSession.getMapper(TrChlBizTypMapper.class);
		List<TrChlBizTyp> trChlBizTyps = trChlBizTypMapper.queryAllOpenOrgChl();
		
		//查询指定机构指定通道指定对账日期完成对账的数量
		TePChlSettMapper tePChlSettMapper = sqlSession.getMapper(TePChlSettMapper.class);
		List<TePChlSett> tePChlSetts = tePChlSettMapper.queryFinishChkAOrgChl(trChlBizTyps,chkAD);
		
		//判断所有机构对应的通道是否对完账
		if(trChlBizTyps.size() != tePChlSetts.size()){
			String errMsg = String.format("机构与通道侧还未完成对账，无法生成业务系统[%s]的[%s]通道[%s]日对账文件", bizSysId,pCId,DateUtil.date3Str(chkAD));
			throw new BaseException(RespCd.NCPO6410,errMsg);
		}
		
	}

	/**
	 *
	 * Description:核对总笔数和总金额是否一致
	 *
	 * @author jjw
	 * @data 2019年11月07日
	 */
	protected abstract void chkNumAndAmt();
	
	/**
	 * 
	 * Description:根据已完成的步骤，开始未完成的步骤
	 *
	 * @author jjw
	 * @data 2019年9月12日
	 */
	private void chkAByStep(){
		
		long start = 0;
		long end = 0;

		//文件生成与上传应该在一个步骤中完成，不然容器更换后，生成好的文件就不存在了，上传肯定会失败
		switch(step){
			case "0":
				start = System.currentTimeMillis();
				sumData();
				end = System.currentTimeMillis();
				LOGGER.info("业务系统[{}]的[{}]通道[{}]日汇总对账数据耗时：[{}]ms",bizSysId,pCId,DateUtil.date3Str(chkAD),end - start);
			case "1":
				start = System.currentTimeMillis();
				genF();
				end = System.currentTimeMillis();
				LOGGER.info("业务系统[{}]的[{}]通道[{}]日生成对账文件耗时：[{}]ms",bizSysId,pCId,DateUtil.date3Str(chkAD),end - start);
				upldF();
			case "2":
				ntcBizSys();
		}
		
	}
	
	/**
	 * 
	 * Description:更新对账进度
	 *
	 * @author jjw
	 * @data 2019年9月24日
	 */
	protected void updateChkASchedule(String step,String chkASta,Date chkAFinTm,String chkAFilePath,String chkAFileNm,String errMsg){
		
		TeBizSettMapper teBizSettMapper = sqlSession.getMapper(TeBizSettMapper.class);
		teBizSett.setStep(step);
		teBizSett.setChkASta(chkASta);
		teBizSett.setChkAFinTm(chkAFinTm);
		teBizSett.setChkAFilePath(chkAFilePath);
		teBizSett.setChkAFileNm(chkAFileNm);
		teBizSett.setErrMsg(errMsg);
		teBizSett.setUpdTm(new Date());
		teBizSettMapper.updateBizChlChkA(teBizSett);
		
	}
	
	/**
	 * 
	 * Description:汇总数据到汇总表
	 *
	 * @author jjw
	 * @data 2019年9月12日
	 */
	protected abstract void sumData();
	
	/**
	 * 
	 * Description:生成对账文件
	 *
	 * @author jjw
	 * @data 2019年9月13日
	 */
	protected abstract void genF();
	
	/**
	 * 
	 * Description:上传对账文件tar包
	 *
	 * @author jjw
	 * @data 2019年9月16日
	 */
	protected abstract void upldF();
	
	/**
	 * 
	 * Description:向业务系统发送下载文件通知
	 *
	 * @author jjw
	 * @data 2019年9月16日
	 */
	protected abstract void ntcBizSys();

}
